﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace MineLib
{
    /// <summary>
    /// Variables class.
    /// </summary>
    public static class Vars
    {
        public static string AuthServer = @"https://authserver.mojang.com/";
    }
    public static class MCLib
    {
        /// <summary>
        /// Makes a HTTP POST request to Mojang's authentication server, with specified endpoint.
        /// </summary>
        /// <param name="JSON">JSON, in JObject, to post.</param>
        /// <param name="endpoint">Endpoint of the authserver, such as "invalidate"</param>
        /// <returns>Returns the response JSON in string format</returns>
        public static string PostHTTP(JObject JSON, string endpoint)
        { // endpoint = authenticate, invalidate, etc.
            string response = null;
            try
            {
                var req = (HttpWebRequest)WebRequest.Create(Vars.AuthServer + endpoint);
                req.ContentType = "application/json";
                req.Method = "POST";
                using (var sw = new StreamWriter(req.GetRequestStream()))
                {
                    sw.Write(JSON.ToString());
                    //Console.WriteLine(response);
                }
                var resp = (HttpWebResponse)req.GetResponse();
                using (var sr = new StreamReader(resp.GetResponseStream()))
                {
                    response = sr.ReadToEnd();
                }
                resp.Close();
            }
            catch (WebException x)
            {
                string receivedError = null;
                int i;
                byte[] buffer = new byte[2048];
                //Console.WriteLine(x.Response.Headers);
                //Console.WriteLine(x.Response.ContentType);
                var reas = x.Response.GetResponseStream();
                while ((i = reas.Read(buffer, 0, buffer.Length)) != 0)
                {
                    receivedError = System.Text.Encoding.ASCII.GetString(buffer, 0, i);
                }
                reas.Close();
                //Console.WriteLine(receivedError);
                Console.WriteLine();
                return receivedError;
            }
            return response;
        }
        /// <summary>
        /// User can supply the JSON returned from Authenticate(), Refresh(), etc. to find any errors
        /// </summary>
        /// <param name="json">The JSON returned from any string-returning method</param>
        /// <param name="error">Out String, returns the name of the error (such as "Not Found")</param>
        /// <param name="errorMessage">The message of the error. For example, "Invalid token."</param>
        /// <param name="errorCause">The cause of the error. The only case for this is "UserMigratedException"</param>
        /// <returns>Returns a bool, whether or not the JSON is erroneous. (i.e. contains the "error" JProperty)</returns>
        public static bool FindErrors(string json, out string error, out string errorMessage, out string errorCause)
        {
            JObject js = JObject.Parse(json);
            bool isError = false;
            error = null;
            errorMessage = null;
            errorCause = null;
            if (js["error"] != null)
            {
                isError = true;
                error = js["error"].ToString();
                errorMessage = js["errorMessage"].ToString();
                if (js["cause"] != null)
                {
                    errorCause = js["cause"].ToString();
                }
                return isError;
            }
            else
            {
                return isError; // just to not piss off the compiler
            }
        }
        /// <summary>
        /// Parses JSON downloaded (if the user is too lazy to parse it themselves)
        /// </summary>
        /// <param name="json">JSON to parse. Will return `null` if JSON is broken, so make sure clientToken and accessToken aren't null.</param>
        /// <param name="clientToken">Out string clientToken</param>
        /// <param name="accessToken">Out string accessToken</param>
        public static void ReturnTokens(string json, out string clientToken, out string accessToken)
        {
            try
            {
                JObject j = JObject.Parse(json);
                clientToken = j["clientToken"].ToString();
                accessToken = j["accessToken"].ToString();
                return;
            }
            catch (Newtonsoft.Json.JsonReaderException)
            {
                clientToken = null;
                accessToken = null;
                return;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="accessToken">The "valid" access token to refresh.</param>
        /// <param name="clientToken">The client's token.</param>
        /// <returns>Returns string JSON, containing new accessToken, clientToken.</returns>
        public static string Refresh(string accessToken, string clientToken)
        {
            JObject topost = new JObject(
                new JProperty("accessToken", accessToken),
                new JProperty("clientToken", clientToken));
            string response = PostHTTP(topost, "refresh");
            return response;
        }
        /// <summary>
        /// Sends a POST request to Mojang's invalidation endpoint, so the accessToken cannot be used anymore.
        /// </summary>
        /// <param name="clientToken">The client token</param>
        /// <param name="accessToken">The access token.</param>
        public static void Signout(string clientToken, string accessToken)
        {
            JObject toPost = new JObject(
                new JProperty("accessToken", accessToken),
                new JProperty("clientToken", clientToken));
            string response = PostHTTP(toPost, "invalidate");
            return;
        }
        /// <summary>
        ///  Sends a POST request to Mojang's authentication endpoint and gets the tokens (client token, access token, available usernames)
        /// </summary>
        /// <param name="username">Username of the user in question.</param>
        /// <param name="password">User's password.</param>
        /// <returns>Returns string (JSON) that contains accessToken, clientToken, etc.</returns>
        public static string Authenticate(string username, string password)
        {
            JObject toPost = new JObject(
                new JProperty("agent",
                    new JObject(
                        new JProperty("name", "Minecraft"),
                        new JProperty("version", 1))),
                new JProperty("username", username),
                new JProperty("password", password));
            return PostHTTP(toPost, "authenticate");
        }
    }
}
